---
title: 零基础实战：用 Docker 启动 dockermailserver 绕过 25 端口封锁搭建个人邮箱服务器
description: "手把手教你用 Docker Mailserver + 中继方案，在被封锁 25 端口的服务器上搭建全功能邮箱系统，支持 SSL 加密收发邮件，解决只能接受不能发送的问题"
date: "2025-05-01"
lang: zh
tags:
  - Docker
  - 邮件服务器
category: 技术
cover: https://cdn.qladgk.com/images/20250508163650687.png
---

import Image from "@/components/mdx/Image";

<Image
  src="https://cdn.qladgk.com/images/20250508163650687.png"
  alt="cover"
  width={999}
  height={527}
  isArticleImage={true}
/>

## 一、为什么需要这套方案？

当你在云服务器尝试搭建邮箱服务时，常常会遇到运营商**封锁 25 端口**的难题，25 端口就像邮局的发货通道，一旦被封，你的邮件服务器就像被没有腿的邮递员，无法对外发送信件

如果无法解除此端口的屏蔽，需要通过其他服务来转发出站邮件，以便其他服务代您发送。我的解决方案是使用 Resend，也就是让人帮他送

并且我需要一台可以创建无限邮箱地址的服务器，因为有个网站会每隔一段时间给账户发兑换码：）方便我白嫖，还有我用邮箱注册了数百个网站，验证码都发在一堆不方便管理，有了自己的服务器后可以按照网站随便注册

比如说，我需要注册 Bilibili，那么邮箱就填 bilibili@qladgk.com ，我不用在服务器中手动添加这个邮箱，可以直接设置一个规则，让不存在的邮箱接受的邮件全部转发给 info@qladgk.com，这样所有的验证码都在 info 这个邮箱。如果哪天我的邮箱泄露，可以很方便的知道是哪个网站对应的邮箱

## 二、准备你的工具箱

| 工具/服务   | 作用说明                             | 获取方式                       |
| ----------- | ------------------------------------ | ------------------------------ |
| Docker      | 容器化部署环境                       | [官网下载](https://docker.com) |
| Resend 账户 | 邮件中继服务（每月免费额度 3000 封） | [注册](https://resend.com)     |
| 域名        | 邮箱域名（如 qladgk.com）            | 域名注册商购买                 |
| SSL 证书    | 加密通信保障                         | 使用受信任的 CA 颁布的证书     |

## 三、五步搭建实战

### 步骤 1：创建目录结构

```bash
mkdir -p mail.qladgk.com/docker-data/{dms/config,certs}
cd mail.qladgk.com
```

我个人喜欢在服务器的 `~` 位置创建用具体域名的文件夹来管理，所以就用了 mail.qladgk.com，然后需要确认你的 SSL 证书中有这个域名

### 步骤 2：准备 docker-compose.yml

```yaml
# docker-compose.yml
version: "3.8"

services:
  mailserver:
    image: ghcr.io/docker-mailserver/docker-mailserver:latest
    container_name: mailserver
    # Provide the FQDN of your mail server here (Your DNS MX record should point to this value)
    hostname: mail.qladgk.com # 修改成你的域名
    env_file: mailserver.env
    # More information about the mail-server ports:
    # https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/
    ports:
      - "25:25" # SMTP  (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
      - "143:143" # IMAP4 (explicit TLS => STARTTLS)
      - "465:465" # ESMTP (implicit TLS)
      - "587:587" # ESMTP (explicit TLS => STARTTLS)
      - "993:993" # IMAP4 (implicit TLS)
    volumes:
      - ./docker-data/dms/mail-data/:/var/mail/
      - ./docker-data/dms/mail-state/:/var/mail-state/
      - ./docker-data/dms/mail-logs/:/var/log/mail/
      - ./docker-data/dms/config/:/tmp/docker-mailserver/
      - ./docker-data/certs/:/tmp/certs/ # 修改成你的证书目录
      - /etc/localtime:/etc/localtime:ro
    environment:
      - SSL_TYPE=manual # 如果要手动设置路径证书类型必须是手动
      - SSL_CERT_PATH=/tmp/certs/nginx.crt # 改成你自己的证书地址
      - SSL_KEY_PATH=/tmp/certs/nginx.key # 改成你自己的证书地址
      - DEFAULT_RELAY_HOST=[服务商获取]:端口 # 中继服务主机
      - RELAY_USER=服务商获取 # 中继服务用户名
      - RELAY_PASSWORD=服务商获取 # 中继服务密码
    restart: always
    stop_grace_period: 1m
    # Uncomment if using `ENABLE_FAIL2BAN=1`:
    # cap_add:
    #   - NET_ADMIN
    healthcheck:
      test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"
      timeout: 3s
      retries: 0
```

与[官网的 compose](https://github.com/docker-mailserver/docker-mailserver/blob/master/compose.yaml) 对比只修改了这些部分：

```yaml
hostname: mail.qladgk.com # 修改为你的域名
volumes:
  - ./docker-data/certs/:/tmp/certs/ # 准备你的域名证书
environment:
  - SSL_TYPE=manual # 如果要自定路径证书类型必须选手动
  - SSL_CERT_PATH=/tmp/certs/certs.crt # 根据自己的证书名设定
  - SSL_KEY_PATH=/tmp/certs/certs.key # 根据自己的证书名设定
  - DEFAULT_RELAY_HOST=[服务商获取]:端口 # 中继服务主机
  - RELAY_USER=服务商获取 # 中继服务用户名
  - RELAY_PASSWORD=服务商获取 # 中继服务密码
```

然后还需要将[官网的 mailserver.env](https://github.com/docker-mailserver/docker-mailserver/blob/master/mailserver.env) 下载下来，这个文件尽量不要改动，东西很多容易错，我们就用 Docker 的环境变量就好，而且环境变量优先于 mailserver.env

### 步骤 3：配置 Postfix 中继主机

注册好 Resend 后添加域名并且使用 DNS 验证

<Image
  src="https://cdn.qladgk.com/images/20250501132950176.png"
  alt="设置 Resend DNS 解析"
  width={888}
  height={527}
  isArticleImage={true}
/>

然后需要等待验证通过，可能需要两三分钟

<Image
  src="https://cdn.qladgk.com/images/20250501132802073.png"
  alt="Resend DNS 验证"
  width={888}
  height={527}
  isArticleImage={true}
/>

然后就可以设置主机、用户名、密码了，修改 docker-compose.yml

```yaml
- DEFAULT_RELAY_HOST=[smtp.resend.com]:587
- RELAY_USER=resend
- RELAY_PASSWORD=创建一个 api 就是密码
```

以上配置是根据 Resend 设置，在官网的这个地方

<Image
  src="https://cdn.qladgk.com/images/20250501133920037.png"
  alt="Resend STMP 服务器"
  width={888}
  height={527}
  isArticleImage={true}
/>

默认用户名应该都是 resend，密码就是你创建的 apikey

### 步骤 4 ：初始化邮件服务器

```bash
# 首次启动初始化
docker compose up -d

# 在 120 秒内创建管理员邮箱
docker exec mailserver setup email add postmaster@qladgk.com <你的邮箱密码>

# 然后创建一个自己用的邮箱
docker exec mailserver setup email add hi@qladgk.com <你的邮箱密码>

# 生成 DKIM 签名
docker exec -it mailserver setup config dkim
```

将 `docker-data/dms/config/opendkim/keys/qladgk.com/mail.txt` 里 `（）` 中的复制出来，并删掉其中的 `""` 和 `多余的空格` 拼接成一行备用

比如生成的 txt 如下

<Image
  src="https://cdn.qladgk.com/images/20250501134947572.png"
  alt="mail.txt"
  width={888}
  height={527}
  isArticleImage={true}
/>

拼接之后的 txt 如下：

```txt
v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAybULjMm1Pv2qn8l0SIan+I8NpBTanNQaVFNVLvR5We9QITGAfxJtVUJv9e9e0BV3XVR/hDWRwxfw4KG6szRzUB1VABp4knYgwr1i3UNgKag3pSgTS4v1k5BccwIod/UK/E6+6iRiIzVoTikd3eSuygDAnAf+/FbZcnVF2Y/W4BvC9+aOyyDUYVCsSmPMgK99fm6fj5Q1LGMAvDapZ4XP+A7aFIHOL+AffPquPcsPAymJgC9guOJH4+D+ejqL5T6loTIljcGzcbavd82sRt01BxX3vRWqsqQ4K5I3yNVX5aLm1vrk5EIssGlyKxjjW9c2+6N7TUz6BD78AaKQmlBlfwIDAQAB
```

### 步骤 5：配置 DNS 记录

| 类型 | 名称             | 值                 | 说明            |
| ---- | ---------------- | ------------------ | --------------- |
| A    | mail             | 你的服务器 ip      | 邮件路由        |
| MX   | @                | mail.qladgk.com    | 优先级设置成 10 |
| TXT  | @                | v=spf1 mx -all     | SPF 发件人验证  |
| TXT  | \_dmarc          | v=DMARC1; p=none;  | DMARC 记录      |
| TXT  | mail.\_domainkey | 刚才准备的那串 txt | DKIM 记录       |

算上开始配置的 Resend 的 DNS ，你的 DNS 解析应该如下

| 类型 | 名称               | 值                 | 说明            |
| ---- | ------------------ | ------------------ | --------------- |
| A    | mail               | 你的服务器 ip      | 邮件路由        |
| MX   | @                  | mail.qladgk.com    | 优先级设置成 10 |
| TXT  | @                  | v=spf1 mx -all     | SPF 发件人验证  |
| TXT  | \_dmarc            | v=DMARC1; p=none;  | DMARC 记录      |
| TXT  | mail.\_domainkey   | 刚才准备的那串 txt | DKIM 记录       |
| ---- | ----------------   | ------------------ | --------------- |
| MX   | send               | Resend 提供的值    | 优先级设置成 10 |
| TXT  | send               | Resend 提供的值    | SPF 发件人验证  |
| MX   | resend.\_domainkey | Resend 提供的值    | DKIM 记录       |

这里同时配置了两个邮箱的 SPF/DKIM ，因为最后邮件是 Resend 用 @send.qladgk.com 这个邮箱地址代发，那么 send.qladgk.com 子域的 SPF/DKIM 记录确实需要按 Resend 要求配置。同时，根域名 @ 的 DMARC 记录对于邮件 From: 是 user@qladgk.com 的情况仍然重要，它需要与 send.qladgk.com 的 SPF/DKIM 对齐（通常是宽松对齐）。

## 四、验证你的邮箱系统

### 发送测试邮件

使用雷鸟客户端，添加邮箱账户和密码

<Image
  src="https://cdn.qladgk.com/images/20250501140639739.png"
  alt="雷鸟客户端"
  width={888}
  height={527}
  isArticleImage={true}
/>

这里的收件和发件都是 `mail.qladgk.com` 是没有问题的

添加账户后给你的 163 或者 qq 尝试发送邮件，可以看到成功收到

<Image
  src="https://cdn.qladgk.com/images/20250501141024406.png"
  alt="发送邮件测试"
  width={888}
  height={527}
  isArticleImage={true}
/>

发件人是 hi@qladgk.com 后面显示的是由 xxxxx@send.qladgk.com 代发

测试 163 直接回复

<Image
  src="https://cdn.qladgk.com/images/20250501190904898.png"
  alt="直接回复测试"
  width={888}
  height={527}
  isArticleImage={true}
/>

可以看到直接回复的是 origin 地址而不是代发地址

给 https://www.mail-tester.com/ 发送测试邮件，查看邮件评分

<Image
  src="https://cdn.qladgk.com/images/20250501141355554.png"
  alt="邮件评分测试"
  width={888}
  height={527}
  isArticleImage={true}
/>

同时你也能看见 Resend 中帮你转发的邮件

<Image
  src="https://cdn.qladgk.com/images/20250501141447566.png"
  alt="Resend 记录"
  width={888}
  height={527}
  isArticleImage={true}
/>

### 接收邮件测试

<Image
  src="https://cdn.qladgk.com/images/20250501142004838.png"
  alt="接受邮件测试"
  width={888}
  height={527}
  isArticleImage={true}
/>

发现我们的 hi@qladgk.com 邮箱成功接收到 163 发来的邮件，但是 Resend 中没有接收记录，后面会解释

## 五、原理解释

### 涉及的服务器与 DNS 配置

自建服务器配置（mail.qladgk.com）

| 类型 | 完整主机名                  | 值示例             | 作用说明                                          |
| ---- | --------------------------- | ------------------ | ------------------------------------------------- |
| A    | mail.qladgk.com             | 11.22.33.44        | 邮件服务器 IP 地址                                |
| MX   | @qladgk.com                 | mail.qladgk.com    | 声明主域名的邮件服务器地址（优先级 10）           |
| TXT  | @qladgk.com                 | v=spf1 mx -all     | 仅允许 mail.qladgk.com 的 IP 发送邮件（防御伪造） |
| TXT  | mail.\_domainkey.qladgk.com | v=DKIM1; p=公钥... | 邮件数字签名公钥（防止邮件被篡改）                |

Resend 中继配置（send.qladgk.com）

| 类型 | 完整主机名                    | 值示例                                | 作用说明                                        |
| ---- | ----------------------------- | ------------------------------------- | ----------------------------------------------- |
| MX   | send.qladgk.com               | feedback-smtp.us-east-1.amazonses.com | Resend 要求的特殊 MX 记录（用于建立发送域身份） |
| TXT  | send.qladgk.com               | v=spf1 include:amazonses.com ~all     | 授权 Resend 的服务器代发邮件                    |
| TXT  | resend.\_domainkey.qladgk.com | k=rsa; p=公钥...                      | Resend 提供的 DKIM 公钥（验证邮件来源合法性）   |

全局反垃圾邮件策略

| 类型 | 完整的主机名称     | 值示例            | 作用                                             |
| ---- | ------------------ | ----------------- | ------------------------------------------------ |
| TXT  | \_dmarc.qladgk.com | v=DMARC1; p=none; | 邮件验证策略（告知收件方如何处理验证失败的邮件） |

也就是说我们一共在解析中设置了两个 MX 方式，所以会出现两个邮箱地址 比如 hi@qladgk.com 和 hi@send.qladgk.com

### 涉及到的端口和认证信息

为了让主机认证我们，需要提供端口和账户密码

在 IMAP 邮件服务器中常见的端口分别如下

| 端口    | 协议               | 使用场景                         | 加密方式 |
| ------- | ------------------ | -------------------------------- | -------- |
| **25**  | SMTP               | 服务器之间的邮件传输             | 无       |
| **465** | SMTP over SSL      | 客户端到服务器的加密邮件发送     | SSL/TLS  |
| **587** | SMTP with STARTTLS | 客户端到服务器的加密邮件发送     | STARTTLS |
| **143** | IMAP               | 客户端从服务器接收邮件（不加密） | 无       |
| **993** | IMAP over SSL      | 客户端从服务器加密接收邮件       | SSL/TLS  |

这里容易混淆，需要慢慢理解

```json
  ┏━━━━━━━━━━ Submission ━━━━━━━━━━━━━┓┏━━━━━━━━━━━━━ Transfer/Relay ━━━━━━━━━━━┓

                            ┌─────────────────────┐                    ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
MUA ----- STARTTLS -------> ┤(587)   MTA ╮    (25)├ <-- cleartext ---> ┊ Third-party MTA ┊
    ----- implicit TLS ---> ┤(465)       │        |                    └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
    ----- cleartext ------> ┤(25)        │        |
                            |┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄|
MUA <---- STARTTLS -------- ┤(143)   MDA ╯        |
    <---- implicit TLS ---- ┤(993)                |
                            └─────────────────────┘

  ┗━━━━━━━━━━ Retrieval ━━━━━━━━━━━━━━┛
```

> MUA 是客户端、MTA 是服务器

发邮件过程会经理 MUA - MTA - MTA - MUA 这过程，我们所说的加密邮件指的是 MTA 与 MUA 之间的过程，而 MUA 之间的传输我们不能干预

<Image
  src="https://cdn.qladgk.com/images/20250501183820433.png"
  alt="邮件工作流程图"
  width={888}
  height={527}
  isArticleImage={true}
/>

之所以之前只能接受邮件而不能发送，是因为服务器封锁 25 端口的出站流量，而没有封锁入站流量。如下图：

<Image
  src="https://cdn.qladgk.com/images/20250501182513349.png"
  alt="25 端口测试"
  width={888}
  height={527}
  isArticleImage={true}
/>

可以看到用服务器通过 25 去访问别人的 stmp 会失败，但是个人电脑上去访问服务器的 25 则成功

接下来是认证信息

其中邮箱客户端（雷鸟）里：

- imap 主机是 mail.qladgk.com、端口是 993（SSL/TLS 加密方式）、用户名是邮箱、密码是邮箱密码
- smtp 主机是 mail.qladgk.com、端口是 587（STARTTLS 加密方式）、用户名是邮箱、密码是邮箱密码

因为接受邮件只有 993 有加密，所以选他。关于 465 和 587 选谁网上说法不一，但是 587 是现在邮箱常见默认端口，所以选他

而我们的 Resend 的 SMTP 的认证信息则直接存放在 docker-mailserver 里

```yaml
# 配置中继
relayhost = [smtp.resend.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = static:resend的用户名:resend的密码
smtp_sasl_security_options = noanonymous
```

这里的 587 指的是我们的邮件服务器现在作为客户端与 Resend 服务端通过 STARTTLS 加密方式将邮件传到他们的 STMP 上

### 邮件发送流程（中继模式）：

1. 客户端提交：雷鸟通过加密连接将邮件提交到 mail.qladgk.com（587 端口），使用邮箱账户密码认证
2. 中继路由决策：Postfix 检测到目标为外部域（如 163.com），根据 relayhost = [smtp.resend.com]:587 启动中继流程
3. 中继服务器认证：使用 SASL 认证机制，通过 Resend 提供的 API Key 完成身份验证：
4. 信封发件人重写：Resend 会将信封发件人（Envelope From）改为 xxx@send.qladgk.com，这是邮件显示"代发"的原因
5. 最终投递：Resend 通过 25 端口将邮件投递到目标服务器，完全绕开本地服务器的出站限制

所以最后收到的邮件的代发人是 xxx@send.qladgk.com

### 邮件接收流程

1. MX 记录查询：外部服务器（163）给 hi@qladgk.com 发邮件时通过 MX 记录找到他的邮件服务器（mail.qladgk.com）
2. 直接投递：发送方通过 25 端口直连你的服务器（云厂商通常允许入站 25 端口）
3. 本地存储：Postfix 将邮件存入 /var/mail/ 目录下的用户邮箱
4. 客户端同步：雷鸟通过 IMAPS（993 端口）加密拉取邮件

### 为什么接收邮件不经过 Resend？

接收流程是标准的邮件服务器间直接通信（Server to Server），仅依赖：

- 正确的 MX 记录指向
- 25 端口的入站连接（通常云厂商允许入站 25 端口）
- 有效的 SSL 证书

而发送时需要出站 25 端口连接（被禁止），因此需要通过中继 "借道" 发送。

### Resend 的角色

Resend 在本方案中充当邮件中继服务器，主要负责将你的邮件投递到目标邮箱服务器。

通过配置 Postfix 的 relayhost 参数，将本地邮件服务器的发送任务委托给 Resend 的 SMTP 服务器。

Resend 提供了每月 3000 封免费额度，对于个人用户或小型团队来说，基本可以满足日常需求。

## 六、常见问题排雷

### 1. 邮件进入垃圾箱

- 检查 SPF/DKIM/DMARC 记录
- 申请反向解析 PTR

其中阿里云的 PTR 很好申请，发个工单就行，三分钟分钟之内添加好

<Image
  src="https://cdn.qladgk.com/images/20250501200710220.png"
  alt="阿里云 PTR 申请"
  width={888}
  height={527}
  isArticleImage={true}
/>

我是这么发的：

主题：

```markdown
申请添加PTR记录
```

内容：

```markdown
尊敬的阿里云售后工程师：
您好！我正在搭建邮件服务器，需要为我的服务器添加 PTR 记录，以便提高邮件的送达率。现向您提交申请，以下是相关信息：
ECS 实例所属地域：[具体地域，如北京、深圳等]
ECS 实例的公网 IP 地址：[公网IP地址]
对应的域名：[域名]
请协助我完成 PTR 记录的添加，谢谢！
```

### 2. 客户端连接失败

```bash
# 检查端口监听状态
docker exec mailserver netstat -tulpn | grep -E '25|587'
```
